package hrd

import "fmt"
import "strings"
import "sync/atomic"

type Solver struct {
    first        uint64
    board2board  map[uint64]uint64
    index2boards [][]uint64
}

func NewSolver(board uint64) *Solver {
    self := &Solver{board, nil, nil}
    self.board2board = map[uint64]uint64{board: 0}
    self.index2boards = [][]uint64{[]uint64{board}}
    return self
}

func (self *Solver) MoveListToBoard(board uint64) []uint64 {
    moves := []uint64{}
    for board != 0 {
        moves = append([]uint64{board}, moves...)
        board = self.board2board[board]
    }
    return moves
}

func (self *Solver) Probe() []uint64 {
    if BoxPosition(self.first) == 13 {
        return []uint64{self.first}
    }
    for {
        board := self.ProbeNext()
        if board != 0 {
            return self.MoveListToBoard(board)
        }
    }
    return []uint64{self.first}
}

func (self *Solver) ProbeBoard(board uint64, ch chan *Move, counter *int32) {
    go func() {
        NextBoards(board, ch, counter)
    }()
}
type Move struct {
parent uint64
child uint64
}

func (self *Solver) ProbeNext() uint64 {
    boards := self.index2boards[len(self.index2boards)-1]
    nexts := []uint64{}
    counter := int32(len(boards))
    ch := make(chan *Move, 1024)
    for _, current := range boards {
        self.ProbeBoard(current, ch, &counter)
    }

    for move := range ch {
        if _, present := self.board2board[move.child]; present == false {
            self.board2board[move.child] = move.parent
            nexts = append(nexts, move.child)
            if BoxPosition(move.child) == 13 {
                return move.child
            }
        }
    }
    self.index2boards = append(self.index2boards, nexts)
    if len(nexts) == 0 {
        return self.first
    }
    return 0
}

func Print(board uint64) {
    bs := [][]string{{" ", " ", " ", " "}, {" ", " ", " ", " "}, {" ", " ", " ", " "}, {" ", " ", " ", " "}, {" ", " ", " ", " "}}

    for _, item := range BoardItems(board) {
        mask := item.Mask()
        s := item.Char()
        for position := uint(0); position < 20; position++ {
            if ((1 << position) & mask) != 0 {
                bs[position/4][position%4] = s
            }
        }
    }

    for _, v := range bs {
        fmt.Println(strings.Join(v, ""))
    }

    fmt.Println(".....")
}

func NextBoards(board uint64, ch chan *Move, counter *int32){
    items := BoardItems(board)
    mask := uint32(0)
    for _, item := range items {
        mask |= item.Mask()
    }
    for _, item := range items {
        for _, newItem := range NextItems(item, mask^item.Mask(), true) {
            newBoard := board ^ item.Hash() ^ newItem.Hash()
            ch <- &Move{board, newBoard}
        }
    }
    if atomic.AddInt32(counter, -1) == 0 {
        close(ch)
    }
}
